//
//  SEHttpManager.swift
//  SmartEyeForPatient
//
//  Created by QDSG on 2021/4/12.
//

import Foundation
import Moya
import HandyJSON
import Toast_Swift

enum SEHttpStatus {
    case success
    case auth(_ message: String?)
    case failure(_ message: String?)
}

/// loading 插件
let LoadingPlugin = NetworkActivityPlugin { (type, target) in
    switch type {
    case .began:
        dispatch_async_safely_main_queue {
            topVc?.view.hideToastActivity()
            topVc?.view.makeToastActivity(.center)
        }
    case .ended:
        dispatch_async_safely_main_queue {
            topVc?.view.hideToastActivity()
        }
    }
}

private let mapDataError = "解析数据出现了问题，请重试"

struct SEResponseDataMap<T>: HandyJSON {
    var code: Int = 0
    var data: T?
    var message: String?
    var success: Bool = false
    
    var results: T?
    var resultCount: Int = 0
}

class SEHttpManager: NSObject {
    static let shared = SEHttpManager()
    
    @discardableResult
    func request<T: TargetType, M>(_ target: T, model: M.Type, withLoading: Bool = false, completion: ((M?, SEHttpStatus) -> Void)?) -> Cancellable? {
        let provider = MoyaProvider<T>(requestClosure: requestTimeoutClosure(target: target), plugins: withLoading ? [LoadingPlugin] : [])
        print(target.baseURL.absoluteString + target.path)
        return provider.request(target) { (result) in
            guard let completion = completion else { return }
            switch result {
            case .success(let response):
                SELog(try? JSONSerialization.jsonObject(with: response.data, options: .allowFragments))
                guard let model = try? response.mapModel(SEResponseDataMap<M>.self) else {
                    completion(nil, .failure(mapDataError))
                    return
                }
                // TODO: code 待确定
                if model.code == 200 {
                    if model.success {
                        completion(model.data, .success)
                    } else {
                        completion(nil, .failure(model.message))
                    }
                } else if model.code == 401 {
                    self.requestTokenError()
                    completion(nil, .auth(model.message))
                } else if model.resultCount > 0 {
                    completion(model.results, .success)
                } else {
                    completion(nil, .failure(model.message))
                }
            case .failure(let error):
                SELog(error.localizedDescription)
                completion(nil, .failure(error.localizedDescription))
            }
        }
    }
    
    /// 请求超时中间件
    private func requestTimeoutClosure<T: TargetType>(target: T) -> MoyaProvider<T>.RequestClosure {
        let timeOutClosure = { (endPoint: Endpoint, closure: MoyaProvider<T>.RequestResultClosure) in
            if var urlRequest = try? endPoint.urlRequest() {
                urlRequest.timeoutInterval = 20
                closure(.success(urlRequest))
            } else {
                closure(.failure(MoyaError.requestMapping(endPoint.url)))
            }
        }
        return timeOutClosure
    }
    /// 请求 token错误
    private func requestTokenError() {
        UserTools.removeAllUserInfo()
        
        if UIViewController.current is SELoginViewController { return }
        
        let vc = SELoginViewController()
        let nav = SENavigationController(rootViewController: vc)
        mainWindow?.rootViewController = nav
    }
}

// MARK: - 请求结果模型解析

extension Response {
    func mapModel<T: HandyJSON>(_ type: T.Type) throws -> T {
        let jsonString = String(data: data, encoding: .utf8)
        guard let model = JSONDeserializer<T>.deserializeFrom(json: jsonString) else {
            throw MoyaError.jsonMapping(self)
        }
        return model
    }
}
